#include <iostream>
#include <assert.h>
#include <intrin.h>
#include "Board.h"
#include "Types.h"

#include "bitboard.h"
//using namespace std;
using std::cout;
using std::endl;


inline void Board_init(Board & b)
{
	//cout << "Board() constructor called" << endl;
	//pieces = new Piece[64];
	//piecesBB = new Bitboard[14];
	Board_Clear(b);
}

/*
* Create a new board by copy
*/
inline void Board_init(Board & b_src, Board & b_dest)
{
	//cout << "Board(Board & b) constructor called" << endl;
	//pieces = new Piece[64];
	//piecesBB = new Bitboard[14];
	memcpy(b_dest.pieces,b_src.pieces,64);
	//for (int i = 0; i < 64; i++)
	//{
	//	pieces[i]= b.pieces[i];
	//}
	b_dest.emptyBB = b_src.emptyBB;
	b_dest.occupiedBB = b_src.occupiedBB;

	memcpy(b_dest.piecesBB,b_src.piecesBB,PIECEBB_SIZE);
	//for (int i = 0; i < 14; i++)
	//	piecesBB[i] = b.piecesBB[i];

	//b_dest.colorBB[WHITE] = b_src.colorBB[WHITE];
	//b_dest.colorBB[BLACK] = b_src.colorBB[BLACK];
}


inline void Board_Clear(Board & b)
{
	for (int i = 0; i < 64; i++)
	{
		b.pieces[i] = NO_PIECE;
	}
	b.emptyBB = 0xffffffffffffffff;
	b.occupiedBB = 0;

	for (int i = 0; i < PIECEBB_SIZE; i++)
		b.piecesBB[i] = 0;

	//b.colorBB[WHITE] = b.colorBB[BLACK] = 0;
}


inline void Board_Clear(Board & b, int square)
{
	b.pieces[square] = NO_PIECE;
	b.emptyBB |= ((Bitboard)1 << square);
	b.occupiedBB &= ~((Bitboard)1 << square);

	//b.colorBB[WHITE] &= ~((Bitboard)1 << square);
	//b.colorBB[BLACK] &= ~((Bitboard)1 << square);
}


inline void Board_SetPiece(Board & b, Piece piece, Square square)
{
	assert((square >= 0) && (square <= 63));
	assert(piece != NO_PIECE);

	b.pieces[square] = piece;
	b.piecesBB[(int)piece] |= (Bitboard)1 << square;
	b.occupiedBB |= (Bitboard)1 << square ;
	b.emptyBB &= ~((Bitboard)1 << square);

	//b.colorBB[color_of(piece)] |= (Bitboard)1 << square ;
}


inline Piece Board_GetPiece(Board & b, Square square)
{
	assert((square >= 0) && (square <= 63)); 
	return b.pieces[square];
}


// Should be inline or not ?
inline void Board_DoMove(Board & b, Move move)
{
	// TODO: uncomment this part
	//assert(piecesBB[(int) W_KING] != 0);
	//assert(piecesBB[(int) B_KING] != 0);
	assert(from_sq(move) != to_sq(move));

	Piece piece = b.pieces[from_sq(move)];
	Piece cPiece = b.pieces[to_sq(move)];

	//if(cPiece!=NO_PIECE){
	//cout << "BEFORE DO_MOVE" << endl;
	//Board_DebugDisplay(b);
	//}

	//if((cPiece == W_KING) || (cPiece == B_KING)){
	//	cout << "WARNING !!! illegal move (before)" << endl;
	//	this->DebugDisplay();
	//}

	b.pieces[to_sq(move)] = b.pieces[from_sq(move)];
	b.pieces[from_sq(move)] =  NO_PIECE;

	Bitboard fromBB = (Bitboard)1 << from_sq(move);
	Bitboard toBB = (Bitboard)1 << to_sq(move);
	Bitboard fromToBB = fromBB ^ toBB; // |+
	b.piecesBB[(int)piece] ^= fromToBB;   // update piece bitboard
	//piecesBB[(int)piece%2] ^= fromToBB;   // update white or black color bitboard
	//b.colorBB[color_of(piece)] ^= fromToBB;   // update white or black color bitboard
	if (cPiece ==  NO_PIECE)
	{
		// quiet move
		b.occupiedBB ^= fromToBB;   // update occupied ...
		b.emptyBB ^= fromToBB;   // ... and empty bitboard
	}
	else
	{
		// capture
		b.piecesBB[(int)cPiece] ^= toBB;       // reset the captured piece
		//piecesBB[(int)cPiece%2] ^= toBB;       // update color bitboard by captured piece
		//b.colorBB[color_of(cPiece)] ^= toBB;   // update color bitboard by captured piece
		b.occupiedBB ^= fromBB;     // update occupied, only from becomes empty
		b.emptyBB ^= fromBB;     // update empty bitboard
	}

	//if(cPiece!=NO_PIECE){
	//cout << "AFTER DO_MOVE" << endl;
	//Board_DebugDisplay(b);
	//}

	//cout << "Occupied");
	//BitboardHelper.DebugDisplay(occupiedBB);
	//cout << "empty");
	//BitboardHelper.DebugDisplay(emptyBB);

	//this->DebugDisplay();

	//if((cPiece == W_KING) || (cPiece == B_KING)){
	//	cout << "WARNING !!! illegal move (after)" << endl;
	//	this->DebugDisplay();
	//}

	// TODO: uncomment this part
	//assert(piecesBB[(int) W_KING] != 0);
	//assert(piecesBB[(int) B_KING] != 0);
}

// Should be inline or not ?
inline void Board_UndoMove(Board & b, Move move, Piece piece, Piece cPiece)
{
	//// TODO: uncomment this part
	//assert(piecesBB[(int) W_KING] != 0);
	//assert(piecesBB[(int) B_KING] != 0);
	assert(from_sq(move) != to_sq(move));

	//Board_DebugDisplay(b);

	//Piece piece = pieces[to_sq(move)];

	b.pieces[from_sq(move)] = piece;
	b.pieces[to_sq(move)] = cPiece;

	Bitboard fromBB = (Bitboard)1 << from_sq(move);
	Bitboard toBB = (Bitboard)1 << to_sq(move);
	Bitboard fromToBB = fromBB ^ toBB; // |+
	b.piecesBB[(int)piece] ^= fromToBB;   // update piece bitboard
	//piecesBB[(int)pieceColor] ^= fromToBB;   // update white or black color bitboard
	//b.colorBB[color_of(piece)] ^= fromToBB;   // update white or black color bitboard
	if (cPiece ==  NO_PIECE)
	{
		// quiet move
		b.occupiedBB ^= fromToBB;   // update occupied ...
		b.emptyBB ^= fromToBB;   // ... and empty bitboard
	}
	else
	{
		// capture
		b.piecesBB[(int)cPiece] ^= toBB;       // reset the captured piece
		//piecesBB[(int)cPiece%2] ^= toBB;       // update color bitboard by captured piece
		//b.colorBB[color_of(cPiece)] ^= toBB;   // update white or black color bitboard
		b.occupiedBB ^= fromBB;     // update occupied, only from becomes empty
		b.emptyBB ^= fromBB;     // update empty bitboard
	}

	//
	//cout << "Occupied");
	//BitboardHelper.DebugDisplay(occupiedBB);
	//cout << "empty");
	//BitboardHelper.DebugDisplay(emptyBB);
	//

	//this->DebugDisplay();

	// TODO: uncomment this part
	//assert(piecesBB[(int) W_KING] != 0);
	//assert(piecesBB[(int) B_KING] != 0);
}


inline bool Board_IsSquareEmpty(Board & b, Square square)
{
	return (b.pieces[square] == NO_PIECE);
}

inline bool Board_IsEmptyOrColor(Board & b, Square square, Color color){
	// TODO: most efficient solution below
	//return (1 << square) & b.emptyBB & b.piecesBB[color]; 
	Piece p = b.pieces[square];
	switch(p){
	case NO_PIECE:
		return true;
	case W_PAWN:
	case W_BISHOP:
	case W_KNIGHT:
	case W_ROOK:
	case W_QUEEN:
	case W_KING:
		return color == WHITE;
	}
	return false;

	//// alternate bitboard version (might be faster)
	//return (1 << square) & (b.emptyBB | b.colorBB[color]);
}

inline bool Board_IsEqual(Board & b1, Board & b2)
{
	// TODO: shorter with bitboards
	for (int i = 0; i < PIECEBB_SIZE; i++) 
		if (b1.piecesBB[i] != b2.piecesBB[i]) 
			return false;
	// board should give the same result as for BB (double check)
	for (int i = 0; i < 64; i++) 
		if (b1.pieces[i] != b2.pieces[i]) 
			return false;
	return true;
}

//
//override int GetHashCode()
//{
//int code = 0;
//for (int i = 0; i < 64; i++) code += ((int)this->pieces[i])*(13 ^ i);
//return code;
//
////return this->pieces.GetHashCode();
//}
//

int Board_Evaluate(Board & b)
{
	// TODO: test values. 
	// BE CAREFULL: the coefficients are closely related to the 'Piece' enum
	//const int coeff[] = { 0, 0, 85, -85, 325, -325, 325, -325, 500, -500, 975, -975, 10000000, -10000000 };
	const int coeff[] = { 0, 85, 325, 325, 500, 975, 30000, 0, 0, -85, -325, -325, -500, -975, -30000, 0,0};
	//	enum Piece {
	//  NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
	//  W_PAWN = 1, W_KNIGHT =  2, W_BISHOP =  3, W_ROOK =  4, W_QUEEN =  5, W_KING =  6,
	//  B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
	//};


	//this->DebugDisplay();
	// classic eval part
	int result = 0;
	//Piece p = NO_PIECE; ;
	// 1- Static score
	for (int i = 0; i < 64; i++)
	{
		// option 1 - avoiding NO_PIECE case
		Piece p = b.pieces[i];
		if (p !=  NO_PIECE)
			result += coeff[(int)p];
		//// option 2 - 1 shot increment considering (int)NO_PIECE = 0
		// result += coeff[(int)b.pieces[i]];
	}

	// 2-Mobility (for now same coef for each piece)
	//result += Board_pawns_mobility(b);
	//result += Board_knights_mobility(b);
	//result += Board_bishops_mobility(b);
	//result += Board_rooks_mobility(b);
	//result += Board_queens_mobility(b);
	//result += Board_kings_mobility(b);

	return result;
}

inline int Board_pawns_mobility(Board & b){
	// TODO: implement this method
	return 0;
}

inline int Board_knights_mobility(Board & b){
	// TODO: implement this method
	return 0;
}

inline int Board_bishops_mobility(Board & b){
	// TODO: implement this method
	return 0;
}

inline int Board_rooks_mobility(Board & b){
	// TODO: implement this method
	return 0;
}

inline int Board_queens_mobility(Board & b){
	// TODO: implement this method
	return 0;
}

inline int Board_kings_mobility(Board & b){
	// TODO: implement this method
	return 0;
}


void Board_DebugDisplay(Board & b)
{
	// Board display
	// Useful to detect an incorrect position
	cout << "BOARD" << endl;
	for (int i = 7; i >= 0; i--)
	{
		cout << " +---+---+---+---+---+---+---+---+" << endl;
		for (int j = 0; j < 8; j++)
			cout << " | " << Board_PieceToChar(b.pieces[i * 8 + j]);
		cout << " |" << endl;
	}
	cout << " +---+---+---+---+---+---+---+---+" << endl;

	//// bitboards debug display 
	// Useful to detect an incorrect bitboard usage
	//cout << "BITBOARDS" << endl;
	//for(int i=0;i<16;i++) {
	//	// displays only non-empty
	//	if(piecesBB[i]) {
	//		cout << "BITBOARD [" << "-PNBRQK??pnbrqk -"[i] << "]" <<endl;
	//		Bitboards_print(piecesBB[i]);
	//	}
	//}
}

//NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
//W_PAWN = 1, W_KNIGHT =  2, W_BISHOP =  3, W_ROOK =  4, W_QUEEN =  5, W_KING =  6,
//B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
inline char Board_PieceToChar(Piece p)
{
	//const char pchars[] = " PNBRQK  pnbrqk  ";
	//return pchars[(int)p];
	return " PNBRQK  pnbrqk  "[(int)p];
}

#pragma intrinsic(_BitScanForward) // WARNING: this is not _BitScanForward64
bool Board_IsCheck(Board & b, Color side)
{
	assert(b.piecesBB[(int) W_KING] != 0);
	assert(b.piecesBB[(int) B_KING] != 0);
	//unsigned long square = 0;
	//_BitScanForward(&square, piecesBB[make_piece(side,KING)]);
	//slowBitScanForward(&square, piecesBB[make_piece(side,KING)]);
	Square square = first_1(b.piecesBB[make_piece(side,KING)]);
	return Board_IsCheck(b,square, (Color)(1-side));
}



//
//bool attacksToKing(int square, int side)
//{
//BitboardHelper bbh = BitboardHelper.Instance;
//
//Bitboard opPawns = piecesBB[(int) W_PAWN + side];
//Bitboard opKnights = piecesBB[(int) W_KNIGHT + side];
//Bitboard opRQ = piecesBB[(int) W_QUEEN + side];
//Bitboard opBQ = opRQ;
//opRQ |= piecesBB[(int) W_ROOK + side];
//opBQ |= piecesBB[(int) W_BISHOP + side];
//return (bbh.arrPawnAttacks[side,square] & opPawns)
//| (bbh.arrKnightAttacks[square] & opKnights)
//| (bbh.bishopAttacks(occupiedBB, square) & opBQ)
//| (bbh.rookAttacks(occupiedBB, square) & opRQ)) ;
//}

// Current version uses bitboards for step attacks & indep. ray scans for slinding pieces
bool Board_IsCheck(Board & b, Square square, Color bySide)
{
	assert((square >= 0) && (square < 64));
	// TODO: change this workaround directly in the enum 'Piece'

	Bitboard pawns = b.piecesBB[make_piece(bySide,PAWN)];
	if (StepAttacksBB[make_piece(BLACK-bySide, PAWN)][square] & pawns) 
		return true;

	Bitboard knights = b.piecesBB[make_piece(bySide,KNIGHT)];
	if (StepAttacksBB[make_piece(bySide, KNIGHT)][square] & knights) 
		return true;

	Bitboard king = b.piecesBB[make_piece(bySide,KING)];
	if (StepAttacksBB[make_piece(bySide, KING)][square] & king) 
		return true;

	/*
	Bitboard bishopsQueens = b.piecesBB[make_piece(bySide, QUEEN)]
	| b.piecesBB[make_piece(bySide, BISHOP)];
	Bitboard bishopAttacks = attacks_bb<BISHOP>(square,b.occupiedBB);
	if (bishopAttacks & bishopsQueens)
	{
	//
	//this->DebugDisplay();
	//BitboardHelper.DebugDisplay(occupiedBB,"occupiedBB");
	//BitboardHelper.DebugDisplay(emptyBB, "emptyBB");
	//BitboardHelper.DebugDisplay((bbh.bishopAttacks(occupiedBB, square)),"bishopAttacks");
	//BitboardHelper.DebugDisplay((bbh.bishopAttacks(occupiedBB, square) & bishopsQueens), "bishopAttacks & bishopsQueens");
	//

	return true;
	}
	Bitboard rooksQueens = b.piecesBB[make_piece(bySide, QUEEN)]
	| b.piecesBB[make_piece(bySide, ROOK)];
	Bitboard rookAttacks = attacks_bb<ROOK>((Square)square, b.occupiedBB);
	if (rookAttacks & rooksQueens)
	{

	////this->DebugDisplay();
	//cout << "occupiedBB" << endl;
	//Bitboards_print(occupiedBB);
	//cout << "emptyBB" << endl;
	//Bitboards_print(emptyBB);
	//cout << "rookAttacks" << endl;
	//Bitboards_print(rookAttacks);
	//cout << "rooksQueens" << endl;
	//Bitboards_print(rooksQueens);
	//cout << "rookAttacks & rooksQueens" << endl;
	//Bitboards_print(rookAttacks & rooksQueens);

	return true;
	}*/

	// bishop&queen threat

	// NO BITBOARD PART
	//Piece pking = (bySide == BLACK) ? W_KING : B_KING;
	//Piece oKing = (bySide == BLACK) ? B_KING : W_KING;
	Piece oQueen = (bySide == BLACK) ? B_QUEEN : W_QUEEN;
	Piece oRook = (bySide == BLACK) ? B_ROOK : W_ROOK;
	Piece oBish = (bySide == BLACK) ? B_BISHOP : W_BISHOP;
	//Piece oKnig =( bySide == BLACK) ? B_KNIGHT : W_KNIGHT;
	//Piece oPawn = (bySide == BLACK) ? B_PAWN : W_PAWN;
	Color oColor = bySide;

	File col = file_of(square);
    Rank row = rank_of(square);

	// Up-right moves
	Square sqStart = square + DELTA_NE;
	Square sqEnd = (Square)std::min(64, square + 9 * (8 - col));
	for (Square sq = sqStart; sq < sqEnd; sq += DELTA_NE)
	{
		Piece p = Board_GetPiece(b, sq);
		if ((p == oBish) || (p == oQueen))
			return true;
		if (p != NO_PIECE)
			break;
		/*
		if (Board_IsEmptyOrColor(b,sq, oColor))
		{
			Piece p = Board_GetPiece(b, sq);
			if ((p == oBish) || (p == oQueen))
				return true;
			if (p != NO_PIECE)
				break;
		}
		else break;
		*/
	}

	// Down-right moves
	sqStart = square + DELTA_SE;
	sqEnd = (Square)std::max(-1, square - 7 * (8 - col));
	for (Square sq = sqStart; sq > sqEnd; sq += DELTA_SE)
	{
		Piece p = Board_GetPiece(b, sq);
		if ((p == oBish) || (p == oQueen))
			return true;
		if (p != NO_PIECE)
			break;
		/*
		if (Board_IsEmptyOrColor(b,sq, oColor))
		{
			Piece p = Board_GetPiece(b, sq);
			if ((p == oBish) || (p == oQueen))
				return true;
			if (p != NO_PIECE)
				break;
		}
		else break;
		*/
	}

	// Down-left moves
	sqStart = square + DELTA_SW;
	sqEnd = (Square)std::max(-1, square - 9 * (col + 1));
	for (Square sq = sqStart; sq > sqEnd; sq += DELTA_SW)
	{
		Piece p = Board_GetPiece(b, sq);
		if ((p == oBish) || (p == oQueen))
			return true;
		if (p != NO_PIECE)
			break;
		/*
		if (Board_IsEmptyOrColor(b,sq, oColor))
		{
			Piece p = Board_GetPiece(b, sq);
			if ((p == oBish) || (p == oQueen))
				return true;
			if (p != NO_PIECE)
				break;
		}
		else break;
		*/
	}

	// Up-left moves
	sqStart = square + DELTA_NW;
	sqEnd = (Square)std::min(64, square + 7 * (col + 1));
	for (Square sq = sqStart; sq < sqEnd; sq += DELTA_NW)
	{
		Piece p = Board_GetPiece(b, sq);
		if ((p == oBish) || (p == oQueen))
			return true;
		if (p != NO_PIECE)
			break;
		/*
		if (Board_IsEmptyOrColor(b,sq, oColor))
		{
			Piece p = Board_GetPiece(b, sq);
			if ((p == oBish) || (p == oQueen))
				return true;
			if (p != NO_PIECE)
				break;
		}
		else break;
		*/
	}


	// rook&queen threat
	// Horizontal moves right
	sqStart = square + DELTA_E;
	sqEnd = (Square)(8 * (row + 1));
	for (Square sq = sqStart; sq < sqEnd; sq++)
	{
		Piece p = Board_GetPiece(b, sq);
		if ((p == oRook) || (p == oQueen))
			return true;
		if (p != NO_PIECE)
			break;
		/*
		if (Board_IsEmptyOrColor(b,sq, oColor))
		{
			Piece p = Board_GetPiece(b, sq);
			if ((p == oRook) || (p == oQueen))
				return true;
			if (p != NO_PIECE)
				break;
		}
		else break;
		*/
	}

	// Horizontal moves left
	sqStart = square + DELTA_W;
	sqEnd = (Square)(8 * row - 1);
	for (Square sq = sqStart; sq > sqEnd; sq--)
	{
		Piece p = Board_GetPiece(b, sq);
		if ((p == oRook) || (p == oQueen))
			return true;
		if (p != NO_PIECE)
			break;
		/*
		if (Board_IsEmptyOrColor(b,sq, oColor))
		{
			Piece p = Board_GetPiece(b, sq);
			if ((p == oRook) || (p == oQueen))
				return true;
			if (p != NO_PIECE)
				break;
		}
		else break;
		*/
	}

	// Vertical moves up
	sqStart = square + DELTA_N;
	sqEnd = (Square)64;
	for (Square sq = sqStart; sq < sqEnd; sq+=DELTA_N)
	{
		Piece p = Board_GetPiece(b, sq);
		if ((p == oRook) || (p == oQueen))
			return true;
		if (p != NO_PIECE)
			break;
		/*
		if (Board_IsEmptyOrColor(b,sq, oColor))
		{
			Piece p = Board_GetPiece(b, sq);
			if ((p == oRook) || (p == oQueen))
				return true;
			if (p != NO_PIECE)
				break;
		}
		else break;
		*/
	}
	// Vertical moves down
	sqStart = square + DELTA_S;
	sqEnd = (Square)-1;
	for (Square sq = sqStart; sq > sqEnd; sq+=DELTA_S)
	{
		Piece p = Board_GetPiece(b, sq);
		if ((p == oRook) || (p == oQueen))
			return true;
		if (p != NO_PIECE)
			break;
		/*
		if (Board_IsEmptyOrColor(b,sq, oColor))
		{
			Piece p = Board_GetPiece(b, sq);
			if ((p == oRook) || (p == oQueen))
				return true;
			if (p != NO_PIECE)
				break;
		}
		else break;
		*/
	}


	return false;
}

